#pragma once
#include <iostream>
#include <cstring>
#include <functional>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>

#include "Log.hpp"
#include "InetAddr.hpp"
#include "Thread.hpp"
#include "ThreadPool.hpp"
using namespace log_ns;


using task_t = std::function<void()>;

enum
{
  SOCKET_ERROR = 1,
  BIND_ERROR,
  LISTEN_ERROR
};

const static int gport = 8888;
const static int glistensockfd = -1;
const static int gblcklog = 8;
class TcpServer
{

public:
  TcpServer(uint16_t port = gport) : _port(port), _listensockfd(glistensockfd), _isrunning(false)
  {
  }

  void InitServer()
  {
    // 1.创建socket
    _listensockfd = ::socket(AF_INET, SOCK_STREAM, 0);
    if (_listensockfd < 0)
    {
      LOG(FATAL, "socker create error\n");
      exit(SOCKET_ERROR);
    }
    LOG(DEBUG, "socket create success, sockfd: %d\n", _listensockfd);

    struct sockaddr_in local;
    memset(&local, 0, sizeof(local));

    local.sin_family = AF_INET;
    local.sin_port = htons(_port);
    local.sin_addr.s_addr = INADDR_ANY;
    // 2.绑定sockfd 和 socket addr
    if (::bind(_listensockfd, (struct sockaddr *)&local, sizeof(local)) < 0)
    {
      LOG(FATAL, "bind error \n");
      exit(BIND_ERROR);
    }
    LOG(DEBUG, "bind success \n");

    // 3.因为tcp是面向连接的，就需要tcp未来不断的能够做到获取链接

    // 让套接字设置为listen状态
    if (::listen(_listensockfd, gblcklog))
    {
      LOG(FATAL, "listen error \n");
      exit(LISTEN_ERROR);
    }
    LOG(DEBUG, "listen success \n");
  }

  class ThreadData
  {
  public:
    int _sockfd;
    TcpServer *_self;
    InetAddr _addr;//给InetAddr再添加一个无参构造
  public:
    ThreadData(int sockfd, TcpServer *self, const InetAddr &addr) : _sockfd(sockfd), _self(self), _addr(addr) 
    {
    }
  };

  void Loop()
  {
    // signal(SIGCHLD, SIG_IGN);
    _isrunning = true;
    while (_isrunning)
    {
      struct sockaddr_in client;
      socklen_t len = sizeof(client);

      // 4.获取新链接
      // 从监听套接字获取新的套接字、获取客户端信息
      int sockfd = ::accept(_listensockfd, (struct sockaddr *)&client, &len);
      // 获取连接失败、继续获取
      if (sockfd < 0)
      {
        LOG(WARNING, "accept error\n");
        continue;
      }
      InetAddr addr(client);
      // 获客成功，提供服务
      LOG(INFO, "get a new link, client info : %s, sockfd is: %d\n", addr.AddrStr().c_str(), sockfd);
      // 不靠谱版本
      // Service(sockfd, addr);

      // version1-----多进程版本
      // pid_t id = fork();
      // if(id == 0)
      // {
      //   //child
      //   //注意以前讲过，子进程会继承父进程的文件描述符表，父子进程会指向同一个文件，因此子进程需要关闭监听套接字
      //   ::close(_listensockfd);
      //   //创建了孙子进程，让子进程直接退出，父进程就能等待到子进程
      //   //，此后让孙子进程去执行任务，孙子进程最后会成为孤儿进程，被OS领养,被OS回收
      //   if(fork() > 0) exit(0);

      //   //子进程来处理服务
      //   Service(sockfd, addr);

      //   exit(0);
      // }

      // //father，要求父进程一定关闭sockfd，父进程只需要监听套接字
      // ::close(sockfd);
      // int n = waitpid(id, nullptr, 0);
      // if(n > 0)
      // {
      //   LOG(INFO, "wait child success.\n");
      // }

      // version2-----多线程版本
      // pthread_t tid;
      // ThreadData* td = new ThreadData(sockfd, this, addr);
      // pthread_create(&tid, nullptr, Execute, td);

      // version 3 ---- 线程池版本 int sockfd, InetAddr addr
      task_t t = std::bind(&TcpServer::Service, this, sockfd, addr);
      //把任务添加到任务队列当中
      ThreadPool<task_t>::GetInstance()->Equeue(t);
    }
    _isrunning = false;
  }

  static void *Execute(void *args)
  {
    pthread_detach(pthread_self());

    ThreadData* td = static_cast<ThreadData *>(args);

    td->_self->Service(td->_sockfd, td->_addr);

    delete td;
    return nullptr;
  }

  void Service(int sockfd, InetAddr addr)
  {
    // 长服务
    while (true)
    {
      // 缺点：inbuffer不是动态的，每次只能读取这么多，或者说，明明只发了5个字节，但是却还是要读取1023个字节
      char inbuffer[1024];
      // n是实际读取的字节数                                   // 当做字符串
      ssize_t n = ::read(sockfd, inbuffer, sizeof(inbuffer) - 1); //(留一个\n的位置)
      if (n > 0)
      {
        inbuffer[n] = 0;
        LOG(INFO, "get message from client %s, message: %s\n", addr.AddrStr().c_str(), inbuffer);
        // 1.读取消息
        std::string echo_string = "[server echo]# ";
        echo_string += inbuffer;
        // 2.拼接好信息之后发回去，证明消息接收成功
        ::write(sockfd, echo_string.c_str(), echo_string.size());
      }
      else if (n == 0) // 返回值为0，表示客户端结束（文件中读到尾）
      {
        LOG(INFO, "client %s quit\n", addr.AddrStr().c_str());
        break;
      }
      else
      {
        LOG(ERROR, "read error: %s\n", addr.AddrStr().c_str());
        break;
      }
    }
    ::close(sockfd);
  }

  ~TcpServer()
  {
  }

private:
  uint16_t _port;
  int _listensockfd; // todo
  bool _isrunning;
};
